(edit: To be clear, many of the following concerns have to do with signal integrity caused by board-to-board use of I2C/SPI devices, as Olin correctly points out.)
Unless you have constraints that strongly push you towards fewer wires (we had one project with a hermetically-sealed connector that each additional contact was rather expensive), avoid I2C when possible, and stick with SPI.
SPI is fairly easy to deal with on a hardware and a software basis. In hardware, there are two shared data lines, Master In Slave Out (MISO or SOMI) and Master Out Slave In (MOSI or SIMO), a shared clock generated by the master, and one chip select per device. The CS line goes low, the clock cycles and essentially shifts in input bits and shifts out output bits, until the transaction finishes, at which point the CS line goes high. When their CS line is high, slave devices don't communicate: they ignore the CLK and MOSI lines, and put their MISO pin into a high-impedance state to let someone else use it.
If you have a microcontroller using several SPI devices, and it has a built-in SPI peripheral, send the microcontroller's CS output to a demultiplexer (e.g. 74HC138) and control the address lines to select the device between SPI transactions; you write words to a register to queue them up for output, and read them back after the CS pin is raised high.
Because SPI signals are all unidirectional, they can be buffered, used across an isolation barrier with digital isolators, and can be sent from board to board using line drivers like LVDS. The only thing you have to worry about is the round-trip propagation delay, which will limit your maximum frequency.
I2C is a completely different story. While it's much simpler from a wiring standpoint, with only two wires SCL and SDA, both these lines are shared bidirectional lines that use open-drain devices with an external pullup. There's a protocol for I2C that starts by transmitting a device address, so that multiple devices can be used if each has their own address.
From a hardware standpoint, it is very difficult to use I2C in systems that have any significant noise. In order to buffer or isolate I2C lines, you have to resort to exotic ICs -- yes, they exist, but there aren't many: we used one on one project and realized that you could use one isolator, but you couldn't use two in series -- it used small voltage drops to figure out which side was the driving end of things, and two series drops were two much.
The logic level thresholds of I2C depend on Vcc so you have to be really careful if you use 3V/3.3V and 5V devices in the same system.
Any signals that use a cable of more than a foot or two have to worry about cable capacitance. Capacitance of 100pf/meter isn't out of the ordinary for multiconductor cable. This causes you to have to slow down the bus, or use lower pullup resistors, to be able to handle the extra capacitance properly and meet the rise time requirements.
So let's say you have a system that you think you've designed well, and you can deal with most of the signal integrity issues, and noise is rare (but still present). What do you have to worry about?
There are a bunch of error conditions you have to be prepared to handle:
Slave device doesn't acknowledge a particular byte. You have to detect this and stop and restart the communications sequence. (With SPI, you can usually read back the data you send if you want to make sure it was received without error.)
You're reading a byte of data from a slave device, and the device is "hypnotized" because of noise on the clock line: You have sent the requisite 8 clocks to read that byte, but because of noise, the slave device thinks it has received 7 clocks, and is still transmitting a 0 on the data line. If the device had received the 8th clock, it would have released the data line high so that the master could raise or lower the data line to transmit an ACK or NACK bit, or the master could transmit a stop (P) condition. But the slave is still holding the data line low, waiting in vain for another clock. If a master is not prepared to try extra clocks, the I2C bus will be stuck in deadlock. While I have used several microcontrollers that handle the normal ACK/NACK conditions, I have never used one that handles this missed clock bit (or extra clock bit) condition successfully, and I've had to exit automatic I2C mode, enter into bit-banging mode, add clocks until the data line is high, and re-enter automatic I2C mode.
The really awful case is when a master is writing data to one slave device, and another slave interprets the device address incorrectly and thinks that the data transmitted is meant for it. We've had I2C devices (I/O expanders) that occasionally have registers set incorrectly because of this. It is nearly impossible to detect this case, and to be robust to noise, you have to periodically set all registers, so that if you do run into this error, at least it will be fixed after a short period of time. (SPI never has this problem -- if you happen to have a glitch on the CS line, it will never persist for long and you won't get data accidentally read by the wrong slave device.)
A lot of these conditions could be handled properly in the protocol if there were error detection (CRC codes), but few devices have this.
I find that I have to build complex software in my I2C master device to handle these conditions. In my opinion, it's just not worth it unless the constraints on wiring force us to use I2C and not SPI.